import { defineStore } from "pinia";
import { openDB } from "idb";
import type { DBSchema, IDBPDatabase } from "idb";

interface Book {
  id: string;
  title: string;
  author?: string;
  createdAt: number;
  updatedAt: number;
  size: number;
  type: "txt" | "epub";
}

interface BookContent {
  id: string;
  content: string; // For txt; for epub, store unpacked manifest later
}

interface Mark {
  id: string;
  bookId: string;
  start: number; // character index
  end: number; // character index
  text: string;
  note?: string;
  createdAt: number;
}

interface ReaderStateRecord {
  bookId: string;
  position: number; // character index
  updatedAt: number;
}

interface ReaderDB extends DBSchema {
  books: {
    key: string;
    value: Book;
    indexes: { byUpdatedAt: number };
  };
  contents: {
    key: string; // bookId
    value: BookContent;
  };
  marks: {
    key: string; // markId
    value: Mark;
    indexes: { byBook: string };
  };
  progress: {
    key: string; // bookId
    value: ReaderStateRecord;
  };
}

let dbPromise: Promise<IDBPDatabase<ReaderDB>> | null = null;
function getDB() {
  if (!dbPromise) {
    dbPromise = openDB<ReaderDB>("we-reader", 1, {
      upgrade(db) {
        const b = db.createObjectStore("books", { keyPath: "id" });
        b.createIndex("byUpdatedAt", "updatedAt");
        db.createObjectStore("contents", { keyPath: "id" });
        const m = db.createObjectStore("marks", { keyPath: "id" });
        m.createIndex("byBook", "bookId");
        db.createObjectStore("progress", { keyPath: "bookId" });
      },
    });
  }
  return dbPromise;
}

function uuid() {
  return Math.random().toString(36).slice(2) + Date.now().toString(36);
}

export type { Mark };

export const useBooksStore = defineStore("books", {
  state: () => ({
    books: [] as Book[],
    loading: false,
  }),
  actions: {
    async loadBooks() {
      const db = await getDB();
      this.books = await db.getAll("books");
      this.books.sort((a, b) => b.updatedAt - a.updatedAt);
    },
    async importFiles(files: FileList | File[]) {
      const db = await getDB();
      this.loading = true;
      try {
        for (const file of Array.from(files)) {
          const ext = file.name.split(".").pop()?.toLowerCase();
          if (ext !== "txt") {
            // TODO: extend to epub
            continue;
          }
          const text = await file.text();
          const id = uuid();
          const now = Date.now();
          const book: Book = {
            id,
            title: file.name.replace(/\.[^.]+$/, ""),
            author: "",
            createdAt: now,
            updatedAt: now,
            size: file.size,
            type: "txt",
          };
          const content: BookContent = { id, content: text };
          await db.put("books", book);
          await db.put("contents", content);
        }
        await this.loadBooks();
      } finally {
        this.loading = false;
      }
    },
    async getContent(bookId: string) {
      const db = await getDB();
      return db.get("contents", bookId);
    },
    async saveProgress(bookId: string, position: number) {
      const db = await getDB();
      const rec: ReaderStateRecord = { bookId, position, updatedAt: Date.now() };
      await db.put("progress", rec);
      const book = await db.get("books", bookId);
      if (book) {
        book.updatedAt = Date.now();
        await db.put("books", book);
        await this.loadBooks();
      }
    },
    async getProgress(bookId: string) {
      const db = await getDB();
      const rec = await db.get("progress", bookId);
      return rec?.position ?? 0;
    },
    async addMark(bookId: string, start: number, end: number, text: string, note?: string) {
      const db = await getDB();
      const mark: Mark = { id: uuid(), bookId, start, end, text, note, createdAt: Date.now() };
      await db.put("marks", mark);
      return mark;
    },
    async getMarks(bookId?: string) {
      const db = await getDB();
      if (!bookId) return db.getAll("marks");
      const tx = db.transaction("marks");
      const idx = tx.store.index("byBook");
      return idx.getAll(bookId);
    },
    async deleteMark(id: string) {
      const db = await getDB();
      await db.delete("marks", id);
    },
  },
});
